#!/usr/bin/env python
# A simple .asoundrc configuration editor
#
# This allows you to easily select the default input and output devices
#
# References:
#  http://alsa.opensrc.org/.asoundrc
#  http://forum.skype.com/viewtopic.php?p=57725
#
# Nigel Sim <nigel.sim@gmail.com>
# http://hpc-external.jcu.edu.au/~nigel
#
#
# TODO
#  Read in old config to:
#    1) Get defaults (DONE)
#    2) Preserve existing settings which don't concern us (DONE)
#    3) Indent the output file
#    4) Recording settings

import gtk
import gtk.glade
import re
import gobject
import time
import shutil
import os

class ASoundItem:
	def __init__(self):
		self.name = None
		self.parameters = {}
		self.children = []

	def append(self, item):
		"""
		
		"""
		self.children.append(item)

	def serialise(self):
		"""
		Serialise self, and children
		"""
		out = self.name+' {\n'
		for p in self.parameters:
			out += '\t%s %s'%(p,self.parameters[p])+'\n'
		for d in self.children:
			out += d.serialise()
		out += '}\n'
		return out

class ASoundParser:
	def __init__(self):
		self.root = []
		self.stack = [self.root]
		self.current = None

	def parse(self, input):
		"""
		Parse this file type object
		This is quite krass, as we just look for { and } to
		delimit records. May not work on all files
		"""
		l = input.readline()
		while l != '':
			if l.find('{') != -1:
				if self.current != None:
					self.stack.append(self.current)
				self.current = ASoundItem()
				self.current.name = l[0:l.find('{')].strip()
			elif l.find('}') != -1:
				old = self.stack.pop()
				old.append(self.current)
				self.current = old
			elif len(l.strip()) == 0:
				pass 
			else:
				l = l.strip()
				lItems = l.split(' ')
				self.current.parameters[lItems[0]] = lItems[1]
			l = input.readline()
	def serialise(self, file):
		"""
		Write out to file
		"""
		out = ''
		for d in self.root:
			out += d.serialise()

		file.write( out )
		

class AlsaProperties(gtk.Window):
	devices = None
	DEFAULT_DEVICE = 'pcm.!default'
	def __init__(self):
		self.glade = gtk.glade.XML('galsaproperties.glade')
		self.glade.signal_autoconnect(self)

		self.window1 = self.glade.get_widget('mainWindow')
		self.window1.connect('delete_event', gtk.main_quit)
		
		self.playback = self.glade.get_widget('cmbPlayback')
		self.record = self.glade.get_widget('cmbRecording')

		self.homeDir =  os.getenv('HOME')
		
		self.readDevices()
		self.playback.set_active(self.getDefaultPlayout())
		
		self.window1.show()


	def readDevices(self):
		"""
		Reads all the available devices from /proc/asound/cards
		"""
		f = open('/proc/asound/cards')
		line = f.readline().rstrip()
		idre = re.compile('^[\ ]*\d ')
		namere = re.compile('\[.*\]')
		self.devices = gtk.ListStore(gobject.TYPE_STRING)
		while not line  == '':
			id = idre.match(line)
			if id != None :
				start = line.find('[')
				end = line.find(']')
				self.devices.append([line[start+1:end-1]])
			line = f.readline().rstrip()

		self.playback.set_model(self.devices)
		self.playback.set_text_column(0)
		self.record.set_model(self.devices)
		self.record.set_text_column(0)

	def on_cmdOK_clicked(self, event):
		self.on_cmdApply_clicked(None)
		gtk.main_quit()
		return

	def on_cmdCancel_clicked(self, event):
		gtk.main_quit()
		return

	def on_cmdApply_clicked(self, event):
		"""
		Write a new .asoundrc file
		"""
		# Backup old file
		try:
			shutil.copyfile(self.homeDir+'/.asoundrc',self.homeDir+'/.asoundrc.'+`time.mktime(time.localtime())`)
		except:
			pass
		outputCard = self.playback.get_active()
		
		# Write a really simple asoundrc file
		f = file(self.homeDir+'/.asoundrc','r')
		p = ASoundParser()
		p.parse(f)
		
		# Try to find the default device
		for d in p.root:
			if d.name == self.DEFAULT_DEVICE:
				break
			d = None
		if d == None:
			d = ASoundItem()
			d.name = self.DEFAULT_DEVICE
			d.parameters['type'] = 'plug'
			d.parameters['slave.pcm'] = '"dmix:%d"'%outputCard
			p.root.append(d)

		else:
			d.parameters['slave.pcm'] = '"dmix:%d"'%outputCard
			
		f.close()

		f = file(self.homeDir+'/.asoundrc','w')
		p.serialise(f)
		f.close()
		return

	def getDefaultPlayout(self):
		"""
		Try to find the default playout device id
		"""
		f = file(self.homeDir+'/.asoundrc','rw')
		p = ASoundParser()
		p.parse(f)
		
		id = -1
		# Try to find the default device
		for d in p.root:
			if d.name == self.DEFAULT_DEVICE:
				i = d.parameters['slave.pcm']
				if i.find('dmix:') != -1:
					id = int(i[len('dmix:')+1])
				break
		return id

if __name__ == '__main__':
	x = AlsaProperties()
	gtk.main()
